# -*- coding: utf-8 -*-
# @DATE :  2025/7/24 
# @Author: HQ
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
# from tag_tools import *
import string
from typing import Callable, Dict, List
import numbers
import os
from scipy.ndimage import label
import matplotlib.pyplot as plt
from decimal import Decimal, ROUND_HALF_UP
from collections import defaultdict

# 安全获取文件路径
try:
    current_file = __file__
except NameError:
    current_file = os.getcwd()

current_dir = os.path.dirname(os.path.abspath(current_file))
csv_path = os.path.join(current_dir, 'wh_report_multilingual.csv')

# 安全读取CSV文件
values_key_message = None
encodings_to_try = ['gbk','utf-8', 'utf-8-sig',  'gb18030', 'ANSI']

for enc in encodings_to_try:
    try:
        values_key_message = pd.read_csv(csv_path, encoding=enc)
        print(f"Successfully loaded CSV with encoding: {enc}")
        break
    except (UnicodeDecodeError, FileNotFoundError, pd.errors.EmptyDataError) as e:
        print(f"Failed to load CSV with encoding {enc}: {e}")
        continue
        
if values_key_message is None:
    print(f"Warning: Could not load CSV file from {csv_path}")
    # 创建空的DataFrame作为备选
    values_key_message = pd.DataFrame(columns=['KeyID', '中文简体'])
test_key = False
date_format = '%Y-%m-%d'
document_key = {"C-1": 'keyPeriodForecastsMenstrualFrequencyInterpretation',
                 "C-2": 'keyPeriodForecastsMenstrualFrequencyInsufficientDataInterpretation',
                 "C-3": 'keyPeriodForecastsCycleRegularityInterpretation',
                 "C-4": 'keyPeriodForecastsCycleRegularityInsufficientDataInterpretation',
                 "C-5": 'keyPeriodForecastsMenstrualPeriodDaysInterpretation',
                 "C-6": 'keyPeriodForecastsMenstrualPeriodDaysInsufficientDataInterpretation',
                 "C-7": 'keyPeriodForecastsMenstrualVolumeInterpretation',
                 "C-8": 'keyPeriodForecastsMenstrualVolumeInsufficientDataInterpretation',

                 "T-1": 'keyPeriodForecastsSkinTemperatureInterpretation',
                 "T-2": 'keyPeriodForecastsSkinTemperatureInsufficientDataInterpretation',

                 "S-1": 'keyPeriodForecastsDysmenorrheaInterpretation',
                 "S-2": 'keyPeriodForecastsDysmenorrheaTrendInterpretation',
                 "S-3": 'keyPeriodForecastsSingleSymptomInterpretation',
                 "S-4": 'keyPeriodForecastsMultipleSymptomsDistribution',
                 "S-5": 'keyPeriodForecastsMultipleSymptomsConcentration',


                 "H-1": 'keyPeriodForecastsHRVAndMoodCorrelated',
                 "H-2": 'keyPeriodForecastsHRVAndMoodUncorrelated',
                 "H-3": 'keyPeriodForecastsHasHRVNoMoodInterpretation',
                 "H-4": 'keyPeriodForecastsHRVAndMoodInsufficientDataInterpretation',

}



# 条件函数示例
def condition_cycle(user_data):
    mens_cycle_len6 = user_data.get('mens_cycle_len_6',[])

    if len(mens_cycle_len6) == 1 :
        return 'cycle len is 1'

    elif 1 < len(mens_cycle_len6) <= 6:
        return 'cycle len > 1'

    else :
        return 'False'


def condition_cycle_diff(user_data):
    mens_cycle_len6 = user_data.get('mens_cycle_len_6', [])
    if len(mens_cycle_len6) == 6 :
        return 'cycle len diff'

    elif 1 < len(mens_cycle_len6) <= 6:
        return 'no cycle len diff'

    else:
        return 'False'


def condition_period(user_data):
    mens_period_len6 = user_data.get('mens_period_len_6', [])
    if len(mens_period_len6) == 1 :
        return 'period len is 1'

    else:
        return 'period len > 1'


def condition_bloods_re(data):
    user_bloods = data.get("mens_blood_len_6", [])
    bloods_count = sum(1 for x in user_bloods if x > 0)
    blood_ratio = bloods_count / len(user_bloods) if len(user_bloods) > 0 else 0
    if blood_ratio > 0.5 and len(user_bloods) > 1:
        return 'blood volume is sufficient'

    elif blood_ratio <= 0.5 and len(user_bloods) >= 1:
        return 'blood volume is insufficient'

    else:
        return 'False'


def condition_hrv_mood(data):
    hrv_mood_key = data.get("hrv_mood_key")
    return hrv_mood_key


def condition_symptoms(data):
    symptom_info = data.get("symptom_info", {})
    if len(data.get("user_symptom_records", [])) == 1:
        return 'symptoms_1'

    elif len(data.get("user_symptom_records", [])) >= 2 :
        period_same_type = symptom_info['same_period']
        if period_same_type:
            return 'symptoms_3'
        else:
            return 'symptoms_2'
    else:
        return 'False'



def condition_pain_2(data):
    user_pain_total_cycles = data.get("user_pain_total_cycles", [])
    if len(user_pain_total_cycles) == 0:
        return 'case 1'
    high = all(user_pain_total_cycles[i] < user_pain_total_cycles[i + 1] for i in range(len(user_pain_total_cycles) - 1))
    low = all(user_pain_total_cycles[i] > user_pain_total_cycles[i + 1] for i in range(len(user_pain_total_cycles) - 1))
    if high or low:
        return 'case 2'

    else:
        return 'case 1'

def condition_temp(data):
    # print('输入的体温有多少天',data)
    if data.get("wear_date_ratio", 0) > 0.5:
        return 'temp trend'
    else:
        return 'temp no trend'

template_configs = [
    {
                        "id": 'C-1',
                        "text": "上周期为{last_cycle_len}天，{status}正常范围；近期均值{cycle_mean}天，在{cycle_min}-{cycle_max}天之间波动，{cycle_status}{cycle_continuous_count}周期{cycle_abnormal_status}。",
                        "condition_func": condition_cycle,
                        "condition_value": 'cycle len > 1',
                        "required_keys" : ['mens_cycle_len_6'],
                        "group": "周期长度",
                        "community": "周期统计"
                    },
                    {
                        "id": 'C-2',
                        "text": "上周期为{last_cycle_len}天，{status}正常范围。持续记录，了解自己的周期节奏。",
                        "condition_func": condition_cycle,
                        "condition_value": 'cycle len is 1',
                        "required_keys" : ['mens_cycle_len_6'],
                        "group": "周期长度",
                        "community": "周期统计"
                    },
                    {
                        "id": 'C-3',
                        "text": "近期最长{cycle_max}天、最短{cycle_min}天，差值为{cycle_diff_value}天，周期波动{cycle_diff}，月经节律表现{cycle_regular}。",
                        "condition_func": condition_cycle_diff,
                        "condition_value": 'cycle len diff',
                        "required_keys" : ['mens_cycle_len_6'],
                        "group": "周期长度差异",
                        "community": "周期统计"
                    },
                    {
                        "id": 'C-4',
                        "text": "近期最长{cycle_max}天、最短{cycle_min}天，差值为{cycle_diff_value}天，周期波动{cycle_diff}，月经节律表现{cycle_regular}。目前积累数据较少，变化可能是暂时的，覆盖6次周期以确保统计稳定性。",
                        "condition_func": condition_cycle_diff,
                        "condition_value": 'no cycle len diff',
                        "required_keys" : ['mens_cycle_len_6'],
                        "group": "周期长度差异",
                        "community": "周期统计"
                    },
                    {
                        "id": 'C-5',
                        "text": "上次经期为{last_period_len}天，{period_value}正常范围；近期在{period_min}~{period_max}天之间波动，平均为{period_mean}天，{period_status}{continuous_count}周期{abnormal_status}。",
                        "condition_func": condition_period,
                        "condition_value": 'period len > 1',
                        "required_keys" : ['mens_period_len_6'],
                        "group":"经期天数",
                        "community":"周期统计"
                    },
                    {
                        "id": 'C-6',
                        "text": "上次经期为{last_period_len}天，{period_value}正常范围。持续记录，了解自己的周期节奏。",
                        "condition_func": condition_period,
                        "condition_value": 'period len is 1',
                        "required_keys" : ['mens_period_len_6'],
                        "group": "经期天数",
                        "community": "周期统计"
                    },
                    {
                        "id": 'C-7',
                        "text": "月经量多集中在第{blood_max_date}天和第{blood_other_date}天，较典型模式分布{blood_type}，月经量问题{blood_abnormal}。",
                        "condition_func":condition_bloods_re,
                        "condition_value": 'blood volume is sufficient',
                        "required_keys" : ['mens_blood_len_6'],
                        "group":  "月经量",
                        "community": "周期统计"

                    },
                    {
                        "id": 'C-8',
                        "text": "月经量多集中在第{blood_max_date}天，但有{blood_other_date}天记录不足，无法完整判断是否符合典型量型，建议补全后做进一步分析。",
                        "condition_func": condition_bloods_re,
                        "condition_value": 'blood volume is insufficient',
                        "required_keys" : ['mens_blood_len_6'],
                        "group":  "月经量",
                        "community": "周期统计"

                    },
                    {
                        "id": 'T-1',
                        "text": "预测排卵日后体温{temp_type}，高温相较低温相相差{delta_temp}℃，体温走势{temp_trend}，显示激素节律可能{hormone_type}。",
                        "condition_func": condition_temp,
                        "condition_value": 'temp trend',
                        "required_keys" : ['temp_data',],
                        "group": "皮肤温度",
                        "community": "皮肤温度"

                    },
                    {
                        "id": 'T-2',
                        "text": "周期体温记录仅有{wear_temp_days}天，存在多个数据空缺，无法进行温度变化趋势分析。",
                        "condition_func": condition_temp,
                        "condition_value": 'temp no trend',
                        "required_keys" : ['temp_data',],
                        "group": "皮肤温度",
                        "community": "皮肤温度"

                    },

                    {
                        "id": 'S-1',
                        "text": "近期峰值痛经多分布在经期{pain_phase}，持续{relieve_day}天得到缓解，{typical_text}典型痛经模式。 ",
                        "condition_func": condition_pain_2,
                        "condition_value": 'case 1',
                        "required_keys" : ['user_pain_records'],
                        "group": "痛经",
                        "community": "痛经与症状"

                    },
                    {
                        "id": 'S-2',
                        "text": "近期峰值痛经多分布在经期{pain_phase}，持续{relieve_day}天得到缓解，{typical_text}典型痛经模式，近期痛经症状{pain_type}，呈{pain_trend}趋势。 ",
                        "condition_func": condition_pain_2,
                        "condition_value": 'case 2',
                        "required_keys" : ['user_pain_records','user_pain_total_cycles'],
                        "group":  "痛经",
                        "community": "痛经与症状"

                    },
                    {
                        "id": 'S-3',
                        "text": "通过分析症状记录，近期你出现最多的症状是{max_symptom}，在{max_symptom_period}出现频次高。",
                        "condition_func": condition_symptoms,
                        "condition_value": 'symptoms_1',
                        "required_keys" : ['user_symptom_records'],
                        "group": "其他症状",
                        "community": "痛经与症状"

                    },
                    {
                        "id": 'S-4',
                        "text": "通过分析症状记录，近期你出现最多的症状是{max_symptom}，其次你还需关注{other_symptom}，分别在{max_symptom_period}和{second_symptom_period}出现频次高。",
                        "condition_func": condition_symptoms,
                        "condition_value": 'symptoms_2',
                        "required_keys" : ['user_symptom_records'],
                        "group": "其他症状",
                        "community": "痛经与症状"

                    },
                    {
                        "id": 'S-5',
                        "text": "通过分析症状记录，近期你出现最多的症状是{max_symptom}，其次你还需关注{other_symptom}，都在{max_symptom_period}出现频次高。",
                        "condition_func": condition_symptoms,
                        "condition_value": 'symptoms_3',
                        "required_keys" : ['user_symptom_records'],
                        "group": "其他症状",
                        "community": "痛经与症状"

                    },
                    {
                        "id": 'H-1',
                        "text": "卵泡期HRV均值{hrv_pattern}黄体期，与正常模式{hrv_trend}。心情记录显示正向情绪伴随{pos_des}HRV，负向情绪伴随{neg_des}HRV。",
                        "condition_func": condition_hrv_mood,
                        "condition_value": 'hrv and mood related',
                        "required_keys" : ['hrv_data'],
                        "group":  "hrv与心情",
                        "community": "HRV与心情"

                    },
                    {
                        "id": 'H-2',
                        "text": "卵泡期HRV均值{hrv_pattern}黄体期，与正常模式{hrv_trend}。心情记录显示心情分布与HRV无明显关联。",
                        "condition_func": condition_hrv_mood,
                        "condition_value": 'hrv and mood unrelated',
                        "required_keys" : ['hrv_data'],
                        "group": "hrv与心情",
                        "community": "HRV与心情"

                    },
                    {
                        "id": 'H-3',
                        "text": "卵泡期HRV均值{hrv_pattern}黄体期，与正常模式{hrv_trend}。暂无获取到心情数据，无法分析情绪记录分布与HRV关联。",
                        "condition_func": condition_hrv_mood,
                        "condition_value": 'no mood record',
                        "required_keys" : ['hrv_data'],
                        "group": "hrv与心情",
                        "community": "HRV与心情"

                    },
                    {
                        "id": 'H-4',
                        "text":  "暂无获取到足够的HRV数据，无法分析情绪记录分布与HRV关联。",
                        "condition_func": condition_hrv_mood,
                        "condition_value": 'hrv data ≤50%',
                        "required_keys" : ['hrv_data'],
                        "group": "hrv与心情",
                        "community": "HRV与心情"

                    }
                ]



def get_safe(user_data, field, invalid_fields):
    """
    从 user_data 里安全取值：
    - 如果字段在 invalid_fields 或者为空，则返回 None
    - 否则返回原值
    """
    if field in invalid_fields:
        return None
    value = user_data.get(field)
    if value is None or value == []:
        return None
    return value


def check_abnormal_cycles(data, threshold):

    continuous_count = len(data)
    if continuous_count > 1:
        if type(threshold) is int :
            status = [abs(data[i]) <= threshold for i in range(len(data))]
        else:
            status = [threshold[0] <= abs(data[i]) <= threshold[1] for i in range(len(data))]

        max_count = 1
        current_count = 1
        for i in range(len(status) -1 ):
            if status[i] == status[i + 1]:  # 与前一个状态相同 → 连续
                current_count += 1
            else:  # 状态中断
                break

        period_count = max(max_count, current_count)
        if period_count == 1:
            period_status = "首次"
            conti_count = ''
        elif period_count == 2:
            period_status = "再次"
            conti_count = ''
        else:
            period_status = "已连续"
            conti_count = period_count

        abnormal_status = '未见异常' if status[0] is True else '偏离'

    else:
        period_status = ''
        conti_count = ''
        abnormal_status = ''


    return period_status, conti_count , abnormal_status

def pain_distribution(pain_list):
    """
    分析痛经在经期的分布情况，处理异常输入
    返回痛经分布的描述
    """
    try:
        if not pain_list or not isinstance(pain_list, list):
            return "无数据"
            
        # 过滤有效数据
        valid_pain_list = []
        for x in pain_list:
            if isinstance(x, (int, float)) and x >= 0:  # 允许-1但不参与计算
                valid_pain_list.append(x)
            else:
                valid_pain_list.append(0)  # 无效数据设为0
                
        n = len(valid_pain_list)
        if n == 0:
            return "无数据"
        
        # 检查是否有有效痛经数据
        if all(x <= 0 for x in valid_pain_list):
            return "无疼痛"

        # 三段索引 - 处理边界情况
        if n == 1:
            return "前半段"  # 只有一天数据
        elif n == 2:
            if valid_pain_list[0] >= valid_pain_list[1]:
                return "前半段"
            else:
                return "后半段"
                
        # 正常的三段分割
        third = max(1, n // 3)  # 确保至少有一个元素
        first = valid_pain_list[:third]
        middle = valid_pain_list[third:2*third]
        last = valid_pain_list[2*third:]

        # 各段疼痛总和
        pain_first = sum(x for x in first if x > 0)
        pain_middle = sum(x for x in middle if x > 0)
        pain_last = sum(x for x in last if x > 0)

        total = pain_first + pain_middle + pain_last
        if total == 0:
            return "无疼痛"

        # 占比计算
        ratio_first = pain_first / total if total > 0 else 0
        ratio_middle = pain_middle / total if total > 0 else 0
        ratio_last = pain_last / total if total > 0 else 0

        # 判断分布
        max_ratio = max(ratio_first, ratio_middle, ratio_last)

        if max_ratio > 0.8:  # 超过80%集中在某一段
            if max_ratio == ratio_first:
                return "前半段"
            elif max_ratio == ratio_middle:
                return "中段"
            else:
                return "后半段"
        else:
            return "整段"
            
    except Exception as e:
        print(f"Error in pain_distribution: {e}")
        return "前半段"  # 默认返回值


def get_top_two_blood_days(data):
    """
    参数:
        data: list[tuple]，每个元组 (day, value)，day=日期, value=大小

    返回:
        list[int]，两个日期（按升序排列）
    """
    if not data:
        return []

    # 1. 分组
    groups = defaultdict(list)
    for day, val in data:
        groups[val].append(day)

    # 2. 按 value 排序（大到小）
    sorted_values = sorted(groups.keys(), reverse=True)
    max_group = groups[sorted_values[0]]

    if len(max_group) >= 2:
        # 最大值组就够两个
        result = sorted(max_group)[-2:]
    else:
        # 最大值只有一个 → 取它和次大值的最后一个
        result = [max(max_group)]
        if len(sorted_values) > 1:
            second_group = groups[sorted_values[1]]
            result.append(max(second_group))

    return sorted(result)
class TemplateNode:

    @staticmethod
    def cal_mens_params(user_data: Dict, invalid_fields: list):
        """计算月经相关参数"""
        mens_values = {}
        
        # 输入验证
        if not isinstance(user_data, dict):
            print("Warning: user_data is not a dictionary")
            return mens_values
            
        if not isinstance(invalid_fields, list):
            invalid_fields = []
            
        user_age = user_data.get('user_age', 28)
        # 年龄合理性检查
        if not isinstance(user_age, (int, float)) or user_age < 10 or user_age > 60:
            user_age = 28  # 使用默认值
            
        user_cycles = get_safe(user_data, 'mens_cycle_len_6', invalid_fields)
        if user_cycles is not None:
            last_cycle_len = user_cycles[0] if len(user_cycles) > 0 else None
            mens_cycle_avg_value = Decimal(np.mean(user_cycles))
            mens_cycle_avg = mens_cycle_avg_value.quantize(Decimal('1'), rounding=ROUND_HALF_UP)
            max_cycle_len = np.max(user_cycles)
            min_cycle_len = np.min(user_cycles)
            cycle_len_diff = max_cycle_len - min_cycle_len

            if last_cycle_len < 25:
                mens_status = "短于"
            elif last_cycle_len > 35:
                mens_status = "长于"
            else:
                mens_status = "处于"

            if 26 <= user_age <= 41:
                if cycle_len_diff > 7:
                    cycle_re = "不规律"
                    cycle_diff = "大"
                else:
                    cycle_re = "规律"
                    cycle_diff = "小"
            else:
                if cycle_len_diff > 9:
                    cycle_re = "不规律"
                    cycle_diff = "大"
                else:
                    cycle_re = "规律"
                    cycle_diff = "小"
            cycle_status, cycle_conti_count, cycle_abnormal_status = check_abnormal_cycles(user_cycles, [24,38])
            mens_values.update({
                'last_cycle_len': last_cycle_len,
                'status': mens_status,
                'cycle_regular': cycle_re,
                'cycle_diff_value': cycle_len_diff,
                'cycle_diff': cycle_diff,
                'cycle_mean': mens_cycle_avg,
                'cycle_min': min_cycle_len,
                'cycle_max': max_cycle_len,
                'cycle_status': cycle_status,
                'cycle_continuous_count': cycle_conti_count,
                'cycle_abnormal_status': cycle_abnormal_status
            })

        user_periods = get_safe(user_data, 'mens_period_len_6', invalid_fields)
        if user_periods is not None :
            last_period_len = user_periods[0]
            mens_period_avg_value = Decimal(np.mean(user_periods))
            mens_period_avg = mens_period_avg_value.quantize(Decimal('1'), rounding=ROUND_HALF_UP)
            max_period_len = np.max(user_periods)
            min_period_len = np.min(user_periods)
            period_value = "属于" if last_period_len <= 8 else "超过"

            period_status, conti_count, abnormal_status = check_abnormal_cycles(user_periods, 8)
            mens_values.update({
                'last_period_len': last_period_len,
                'period_mean': mens_period_avg,
                'period_max': max_period_len,
                'period_min': min_period_len,
                'period_value': period_value,
                'period_status': period_status,
                'continuous_count': conti_count,
                'abnormal_status': abnormal_status
            })

        user_bloods = get_safe(user_data, 'mens_blood_len_6', invalid_fields)
        if user_bloods is not None :
            bloods_count = sum(1 for x in user_bloods if x > 0)
            blood_ratio = bloods_count / len(user_bloods)

            # Step 1: 取出血量最高的两天（如果分数相同，取日期靠后的）
            bleeding_with_index = list(enumerate(user_bloods, start=1))  # [(1, 3.2), (2, 5.1), ...]
            # 按照出血量优先排序，若出血量相同，取日期更靠后的（较大）
            top_days = sorted(bleeding_with_index, key=lambda x: (x[1]), reverse=True)

            if blood_ratio > 0.5 and len(user_bloods) > 1:
                # print('血量足够')

                # max_blood_days = top_days_sort[0][0]
                # other_bloods = top_days_sort[1][0]
                max_blood = top_days[0][1]
                blood_days_sort = get_top_two_blood_days(top_days)
                max_blood_days = blood_days_sort[0]
                other_bloods = blood_days_sort[1]

                # Step 2: 判断是否符合典型模式（前3天重，之后减轻）
                first_half = user_bloods[:3]
                second_half = user_bloods[3:]
                # blood_type = "合理" if np.mean([x for x in first_half if x>0]) >= np.mean([x for x in second_half if x>0]) else "略延后减轻"

                blood_type = "合理" if max_blood in first_half and max_blood not in second_half else "略延后减轻"
                # print('血量',user_bloods, first_half, second_half, blood_type)
                # Step 3: 判断是否存在异常（例如有连续5天中高出血量或异常反弹）
                # # 自定义异常规则：超过6天血量高于中位数1.5倍
                # median = np.median(user_bloods)
                # high_days = sum(1 for val in user_bloods if val > 1.5 * median)
                blood_ab = "存在异常" if blood_type == "略延后减轻" else "未见异常"

            else:
                # print('血量不够')
                max_blood_days = top_days[0][0]
                other_bloods = len([i + 1 for i, v in enumerate(user_bloods) if v == -1])
                blood_type = None
                blood_ab = None

            mens_values.update({
                'blood_max_date': max_blood_days,
                'blood_other_date': other_bloods,
                'blood_type': blood_type,
                'blood_abnormal': blood_ab
            })
        # print('月经数据', mens_values)
        return mens_values

    @staticmethod
    def cal_temp_params(user_data, invalid_user_data):
        """计算体温相关参数"""
        temp_type = None
        temp_trend = None
        hormone_type = None

        temp_tag = cal_temp_tag(user_data)
        temp_diff = temp_tag['temp_pattern']
        wear_temp_days = temp_tag['wear_temp_days']
        if temp_diff:

            temp_type = "明显升高" if temp_diff >= 0.3 else "无持续升温"
            if 0 <= temp_diff < 0.3:
                temp_trend = "平稳"
                hormone_type = "分泌失调"
            elif temp_diff >= 0.3:
                temp_trend = "呈双相结构"
                hormone_type = "正常稳定"
            else:
                temp_trend = "呈反相结构"
                hormone_type = "严重紊乱"

        return {'delta_temp': temp_diff,
                'temp_type': temp_type,
                'temp_trend': temp_trend,
                'hormone_type': hormone_type,
                'wear_temp_days': wear_temp_days,
                'low_temp': temp_tag['low_temp'] if temp_trend == '呈双相结构' else None,
                'high_temp': temp_tag['high_temp'] if temp_trend == '呈双相结构' else None,
                'wear_date_ratio': temp_tag['wear_date_ratio'],
                'real_filter_temp': temp_tag['real_filter_temp'] if temp_trend == '呈双相结构' else None,

                }

    @staticmethod
    def cal_hrv_params(user_data,invalid_data):
        return user_data


    @staticmethod
    def cal_symptoms_params(user_data, invalid_fields):
        symptom_value = {}
        pain_values = user_data.get('user_pain_records', [])
        if 'user_pain_records' in invalid_fields or not user_data.get('user_pain_records'):
           pass

        else:
            # 1. 峰值痛经在哪段（前中后）
            n = len(pain_values)
            max_val = max(pain_values)
            peak_days = [i for i, val in enumerate(pain_values) if val == max_val]
            peak_day = max(peak_days)
            pain_phase = pain_distribution(pain_values)

            # 2. 持续几天得到缓解（从峰值开始算，直到疼痛≤1）
            relieve_day = n  # 默认整个周期都没缓解
            for i in range(peak_day, n):
                if pain_values[i] <= 1.0:
                    # print(relieve_day, i, peak_day)
                    relieve_day = i - peak_day   # 包含开始那天
                    break
            # if test_key:
            # print('疼痛峰值',max_val, peak_day,pain_values,relieve_day)
            # 3. 是否符合典型痛经模式
            # 典型模式是：第一天达到高峰，持续2~3天后缓解
            typical = False
            if n >= 4:
                peak_first_3 = max(pain_values[:3])
                if pain_values[0] == peak_first_3 and all(pain_values[i] <= 1.0 for i in range(3, n)):
                    typical = True
            typical_text = "基本符合" if typical else "不符合"

            # symptom_value = {
            #     'pain_phase': pain_phase,
            #     'relieve_day': relieve_day,
            #     'typical_text': typical_text,
            #     'pain_type': pain_type,}

            symptom_value['pain_phase'] = pain_phase
            symptom_value['relieve_day'] = relieve_day
            symptom_value['typical_text'] = typical_text

        pain_total_cycles = user_data.get('user_pain_total_cycles', [])
        if 'user_pain_total_cycles' in invalid_fields or not user_data.get('user_pain_total_cycles'):
            pass  # 或默认提示
        else:
            if all(pain_total_cycles[i] > pain_total_cycles[i + 1] for i in range(len(pain_total_cycles) - 1)):
                pain_type = "加重"
                pain_trend = "恶化"
            elif all(pain_total_cycles[i] < pain_total_cycles[i + 1] for i in range(len(pain_total_cycles) - 1)):
                pain_type = "减轻"
                pain_trend = "改善"
            else:
                pain_type = None
                pain_trend = None

            symptom_value['pain_type'] = pain_type
            symptom_value['pain_trend'] = pain_trend

        if 'user_symptom_records' in invalid_fields or not user_data.get('user_symptom_records'):
            pass  # 或默认提示

        else:
            symptom_values_key = {'腹痛': 1,
                                  '头痛': 2,
                                  '乳房胀痛': 3,
                                  '身体酸痛': 4,
                                  '腰痛': 5,
                                  '腹泻': 6,
                                  '便秘': 7,
                                  '腹胀':8,
                                  '恶心':9,
                                  '消化不良':10,
                                  '呕吐':11,
                                  '潮热':12,
                                  '疲劳':13,
                                  '偏头痛':14,
                                  '背痛':15,
                                  '胃痛':16,
                                  '盆腔痛':17,
                                  '性交疼痛':18,
                                  '排卵痛':19,
                                  '外阴痛':20,
                                  '腿痛':21 ,
                                  '自定义症状':-1}
            value_to_key = {v: k for k, v in symptom_values_key.items()}

            period_values_key = {'mens': '月经期',
                                 'folli': '卵泡期',
                                 'ovula': '排卵窗口',
                                 'lm': '黄体期',
                                 }

            symptom_info = user_data.get('symptom_info',{})

            if symptom_info:
                max_symptom_key = symptom_info['max_symptom_key']
                second_symptom_key = symptom_info['second_symptom_key']
                max_period = symptom_info['max_value']
                second_period = symptom_info['second_value']

                symptom_value['max_symptom'] = value_to_key.get(max_symptom_key)
                symptom_value['other_symptom'] = value_to_key.get(second_symptom_key)
                symptom_value['max_symptom_period'] = period_values_key.get(max_period)
                symptom_value['second_symptom_period'] = period_values_key.get(second_period)


        return symptom_value

    def __init__(self, id: str, text: str, condition_func: Callable[[Dict], bool], condition_value:str, required_keys:list, group: str, community: str ):
        self.id = id
        self.text = text
        self.condition_func = condition_func
        self.condition_value = condition_value
        self.required_keys = required_keys
        self.group = group
        self.community = community

        self.community_calculators = {

            "周期统计": self.cal_mens_params,
            "皮肤温度": self.cal_temp_params,
            "痛经与症状": self.cal_symptoms_params,
            "HRV与心情": self.cal_hrv_params,

        }


    def extract_placeholders(self) -> set:
        formatter = string.Formatter()
        return {field_name for _, field_name, _, _ in formatter.parse(self.text) if field_name}


    def render(self, user_data: Dict, invalid_fields):
        """渲染模板并返回结果"""
        try:
            # 获取当前社区的参数生成函数
            calc_func = self.community_calculators.get(self.community)
            if not calc_func:
                print(f"Warning: Community {self.community} not registered")
                return "", [], {}

            formatter = string.Formatter()
            values_key = []
            used_params = {}
            
            # 获取参数
            try:
                params = calc_func(user_data, invalid_fields)
            except Exception as e:
                print(f"Error calculating parameters for {self.community}: {e}")
                return "", [], {}

            for literal_text, field_name, format_spec, conversion in formatter.parse(self.text):
                if field_name is None:
                    continue

                v = params.get(field_name)
                if v is None:
                    print(f"Missing parameter: {field_name} for template {self.id}")
                    return "", [], {}
                    
                used_params[field_name] = v

                if v == '':
                    values_key.append('')
                elif isinstance(v, numbers.Number):
                    values_key.append(str(v))
                elif isinstance(v, list):
                    v_str = ', '.join(str(x) for x in v)
                    values_key.append(v_str)
                else:
                    # 查找多语言对应
                    if values_key_message is not None:
                        try:
                            matching_rows = values_key_message[values_key_message['中文简体'] == v]
                            if not matching_rows.empty:
                                value_key = matching_rows['KeyID'].values[0]
                                values_key.append(value_key)
                            else:
                                print(f"No translation found for: {v}")
                                values_key.append(str(v))  # 使用原始值作为备选
                        except Exception as e:
                            print(f"Error looking up translation for {v}: {e}")
                            values_key.append(str(v))
                    else:
                        values_key.append(str(v))

            return self.text.format(**used_params), values_key, params
            
        except Exception as e:
            print(f"Error rendering template {self.id}: {e}")
            return "", [], {}


class CommunityGraph:
    def __init__(self):
        self.nodes: List[TemplateNode] = []
        self.communities: Dict[str, List[TemplateNode]] = {}

    def add_node(self, node: TemplateNode):
        self.nodes.append(node)
        self.communities.setdefault(node.community, []).append(node)


    def match_templates(self, user_data: Dict) -> List[TemplateNode]:
        matched_by_group: Dict[str, TemplateNode] = {}
        # print('开始匹配')
        for node in self.nodes:
            # print('节点是什么',node)
            if any(k not in user_data for k in node.required_keys):
                continue

            try:
                result_value = node.condition_func(user_data)
                # print(f"模板 {node.id} 条件判断结果: {result_value}")
            except Exception as e:
                print(f"模板 {node.id} 条件执行报错: {e}")
                continue

            if result_value == node.condition_value:
                # print('存储的内容', node.id,result_value, node.condition_value)
                if node.group not in matched_by_group:
                    matched_by_group[node.group] = node
                    # print('存储的内瓤',node.id)

            # if result:
            #     if node.group not in matched_by_group:
            #         matched_by_group[node.group] = node

        return list(matched_by_group.values())

#--------------------------------------------------------------------------
# 温度
def cal_temp_tag(temp_input):
    """
    体温分区计算高低温区及delta
    :param temp_data: pd.DataFrame

    """
    temp_tag = {}

    delta = None
    real_high_temp = None
    real_low_temp = None
    real_filter_temp = None

    temp_data = temp_input.get('temp_data',[])
    temp_filter = temp_data['filter'].tolist()
    wear_date_ratio = temp_input.get('wear_date_ratio', 0)
    wear_temp_days = temp_input.get('wear_temp_days')
    ovulation_date = temp_input.get('ovul_date')
    # print('排卵日',ovulation_date , temp_data.iloc[0]['date_sleep'])
    low_temp_dates = (ovulation_date - temp_data.iloc[0]['date_sleep']).days

    # print('算参数这里的入参',temp_input)
    if wear_date_ratio > 0.5 and len(temp_data) > 0:

        mean_temp = np.mean(temp_filter)
        min_temp = np.min(temp_filter)
        max_temp = np.max(temp_filter)
        low_temp_x = mean_temp * 0.3 + min_temp * 0.7
        high_temp_x = mean_temp * 0.3 + max_temp * 0.7

        cross_candidates = []
        for i in range(1, len(temp_filter)):
            if temp_filter[i - 1] < mean_temp and temp_filter[i] >= mean_temp:
                cross_candidates.append(i)

        center = low_temp_dates if low_temp_dates >0 else wear_temp_days//2
        if cross_candidates:
            cross_idx = min(cross_candidates, key=lambda x: abs(x - center))
            low_range = cal_lowhigh_period(temp_filter, cross_idx, low_temp_x, 'low')
            high_range = cal_lowhigh_period(temp_filter, cross_idx, high_temp_x, 'high')

        else:
            low_range = None
            high_range = None
            cross_idx = center


        if low_range is not None and high_range is not None and cross_idx is not None:
            real_low_temp = round(np.mean(temp_filter[low_range[0]:low_range[1]+1]),2)
            real_high_temp = round(np.mean(temp_filter[high_range[0]:high_range[1]+1]), 2)
            delta = round(real_high_temp - real_low_temp,2)

        else:
            min_5 = sorted(temp_filter[:center + 1])[:5]
            max_5 = sorted(temp_filter[center:],reverse= True)[:5]
            real_low_temp = round(np.mean(min_5), 2)
            real_high_temp = round(np.mean(max_5), 2)
            delta = round(real_high_temp - real_low_temp, 2)

        temp_data['date_sleep'] = temp_data['date_sleep'].dt.strftime('%Y-%m-%d')
        filter_temp = temp_data.values.tolist()
        real_filter_temp = [[str(value) for value in row] for row in filter_temp]


    temp_tag['temp_pattern'] = delta
    temp_tag['low_temp'] = real_low_temp
    temp_tag['high_temp'] = real_high_temp
    temp_tag['wear_date_ratio'] = wear_date_ratio
    temp_tag['real_filter_temp'] = real_filter_temp
    temp_tag['wear_temp_days'] = wear_temp_days
    print('体温计算的参数', temp_tag) if test_key is True else None

    return temp_tag


def cal_lowhigh_period(temp_filter, cross_idx,temp_x,term_key):
    """
    计算高低温范围
    :param temp_filter: 滤波数据
    :param cross_idx:均值分界线
    :param temp_x: 高低温极值
    :param term_key: 计算高/低 温区， high/low
    """
    term_mask = np.zeros_like(temp_filter, dtype=bool)
    if term_key == 'low':
        term_mask[:cross_idx + 1] = temp_filter[:cross_idx + 1] < temp_x
    else:
        term_mask[cross_idx:] = temp_filter[cross_idx:] > temp_x
    try:
        if term_mask.any():
            # 1. 找出所有 True 连续段
            labeled_array, num_features = label(term_mask)
            # 2. 获取所有段的开始结束位置
            candidates = []
            for i in range(1, num_features + 1):
                indices = np.where(labeled_array == i)[0]
                start, end = indices[0], indices[-1]
                mid = (start + end) // 2
                distance = abs(mid - cross_idx)
                candidates.append((distance, start, end))
            # 3. 找出离 cross_idx 最近的段
            best_segment = min(candidates, key=lambda x: x[0])
            _, term_start, term_end = best_segment
            term_range = (term_start, term_end)
        else:
            term_range = None

    except:
        term_range = None

    return term_range

#--------------------------------------------------------------------------
# hrv
def cal_hrv_tag(data, mens_cycle):
    """
    计算hrv标签，处理异常场景
    为了保持向后兼容，这里返回简化版本
    """
    hrv_tag = {'hrv_pattern': 0, 'hrv_avg': 0}
    
    try:
        # 检查必要的日期字段
        required_keys = ["mens_pred_date", "ovul_pred_date", "mens_next_date"]
        if not isinstance(mens_cycle, dict) or any(mens_cycle.get(k) is None for k in required_keys):
            return hrv_tag

        # 检查HRV数据是否存在且有效
        if data is None or data.empty or 'hrv_avg' not in data.columns:
            return hrv_tag

        mens_date = change_time(mens_cycle, 'mens_pred_date')
        ovula_date = change_time(mens_cycle, 'ovul_pred_date')
        mens_date_next = change_time(mens_cycle, 'mens_next_date')

        folli_hrv_data = data[(data['date_sleep'] >= mens_date) & (data['date_sleep'] < ovula_date)]
        lm_hrv_data = data[(data['date_sleep'] > ovula_date) & (data['date_sleep'] < mens_date_next)]
        hrv_data = data[(data['date_sleep'] >= mens_date) & (data['date_sleep'] < mens_date_next)]

        # 计算均值，处理空数据情况
        if len(hrv_data) > 0:
            hrv_avg = np.mean(hrv_data['hrv_avg'])
            if np.isnan(hrv_avg):
                hrv_avg = 0
        else:
            hrv_avg = 0
            
        # 只有当两个阶段都有数据时才计算差值
        if len(folli_hrv_data) > 0 and len(lm_hrv_data) > 0:
            folli_hrv_avg = np.mean(folli_hrv_data['hrv_avg'])
            lm_hrv_avg = np.mean(lm_hrv_data['hrv_avg'])
            
            if not (np.isnan(folli_hrv_avg) or np.isnan(lm_hrv_avg)):
                hrv_diff = folli_hrv_avg - lm_hrv_avg
            else:
                hrv_diff = 0
        else:
            hrv_diff = 0

        hrv_tag['hrv_pattern'] = hrv_diff
        hrv_tag['hrv_avg'] = hrv_avg

    except Exception as e:
        print(f"Error in cal_hrv_tag: {e}")
        hrv_tag = {'hrv_pattern': 0, 'hrv_avg': 0}

    return hrv_tag

def change_time(mens_cycle, key):
    """
    安全地解析日期字符串，保证返回有效的datetime对象
    如果解析失败，返回当前日期作为备选
    """
    try:
        if not isinstance(mens_cycle, dict):
            return datetime.today()
            
        date_stage = mens_cycle.get(key)
        if not date_stage:
            return datetime.today()
            
        # 尝试解析日期
        if isinstance(date_stage, str):
            return datetime.strptime(date_stage, '%Y-%m-%d')
        elif isinstance(date_stage, datetime):
            return date_stage
        else:
            return datetime.today()
            
    except (ValueError, TypeError):
        # 如果日期格式错误，返回当前日期
        return datetime.today()
    except Exception:
        # 任何其他异常，返回当前日期
        return datetime.today()
"""
def cal_mens_params( user_data: Dict):
    user_cycles = user_data.get('mens_cycles_len_6', [])
    user_periods = user_data.get('mens_period_len_6', [])
    use_bloods = user_data.get('mens_blood_len_6', [])
    user_age = user_data.get('user_age', 28)

    last_cycle_len = user_cycles[-1]
    mens_cycle_avg = np.mean(user_cycles)
    max_cycle_len = np.max(user_cycles)
    min_cycle_len = np.min(user_cycles)
    cycle_len_diff = max_cycle_len - min_cycle_len

    if last_cycle_len < 25:
        mens_status = "短于"
    elif last_cycle_len > 35:
        mens_status = "长于"
    else:
        mens_status = "处于"

    if 26 <= user_age <= 41:
        if cycle_len_diff > 7:
            cycle_re = "不规律"
            cycle_diff = "大"
        else:
            cycle_re = "规律"
            cycle_diff = "小"
    else:
        if cycle_len_diff > 9:
            cycle_re = "不规律"
            cycle_diff = "大"
        else:
            cycle_re = "规律"
            cycle_diff = "小"


    last_period_len = user_periods[-1]
    mens_period_avg = np.mean(user_periods)
    max_period_len = np.max(user_periods)
    min_period_len = np.min(user_periods)
    period_value = "属于" if last_period_len <= 8 else "超过"

    continuous_count = len(user_cycles)
    if continuous_count > 1:
        period_status = f"已连续{continuous_count}"
    else:
        period_status = "首次"

    abnormal_flag = user_data.get('abnormal_flag', False)
    abnormal_status = "偏离" if abnormal_flag else "未见异常"

    # Step 1: 取出血量最高的两天（如果分数相同，取日期靠后的）
    bleeding_with_index = list(enumerate(use_bloods, start=1))  # [(1, 3.2), (2, 5.1), ...]
    # 按照出血量优先排序，若出血量相同，取日期更靠后的（较大）
    top_days = sorted(bleeding_with_index, key=lambda x: (x[1], x[0]), reverse=True)[:2]
    top_blood_days = sorted([d[0] for d in top_days])  # 排序天数，以保证输出 "第n天和第n天"

    # Step 2: 判断是否符合典型模式（前3天重，之后减轻）
    first_half = use_bloods[:3]
    second_half = use_bloods[3:]
    blood_type = "合理" if np.mean(first_half) >= np.mean(second_half) else "略延后减轻"

    # Step 3: 判断是否存在异常（例如有连续5天中高出血量或异常反弹）
    # 自定义异常规则：超过6天血量高于中位数1.5倍

    median = np.median(use_bloods)
    high_days = sum(1 for val in use_bloods if val > 1.5 * median)
    blood_ab = "存在异常" if high_days >= 5 else "未见异常"


    return {
        "last_cycle_len": last_cycle_len,
        "status": mens_status,
        "cycle_regular": cycle_re,
        "cycle_diff": cycle_diff,
        "cycle_mean": mens_cycle_avg,
        "cycle_min": min_cycle_len,
        "cycle_max": max_cycle_len,

        "last_period_len": last_period_len,
        "period_mean": mens_period_avg,
        "period_max": max_period_len,
        "period_min": min_period_len,
        "period_value": period_value,
        "period_status": period_status,
        "abnormal_status": abnormal_status,

        "blood_max_date": top_blood_days,
        "blood_type" : blood_type,
        "blood_abnormal": blood_ab

    }

def cal_temp_params( user_data):

    temp_data = user_data.get("temp_data")
    menstrual_dates = user_data.get("menstrual_dates")

    mens_cycles, error = create_every_cycles(menstrual_dates)

    mens_date = [datetime.strptime(d['mens_date'], date_format) for d in menstrual_dates]
    mens_start = mens_date[0]
    mens_end = mens_date[1]
    mens_date = ( mens_end - mens_start).days
    temp_tag = cal_temp_tag(temp_data, mens_cycles)
    temp_diff = temp_data['temp_pattern']
    temp_type = "明显升高" if temp_diff > 0.3 else "无持续升高"
    if 0 <= temp_diff <= 0.3 :
        temp_trend = "平稳"
        hormone_type = "正常稳定"
    elif temp_diff > 0.3 :
        temp_trend = "双相结构"
        hormone_type = "分泌失调"
    else:
        temp_trend = "异常"
        hormone_type = "严重紊乱"

    return { 'temp_diff':temp_diff ,
             'temp_type' : temp_type,
             'temp_trend':temp_trend ,
             'hormone_type' : hormone_type

    }


def cal_hrv_params(user_data):
    hrv_data = user_data.get("hrv_data")
    menstrual_dates = user_data.get("menstrual_dates")
    mood_records = user_data.get("mood_records", [])
    mood_df = pd.DataFrame(mood_records)
    hrv_data['date_sleep'] = pd.to_datetime(hrv_data['date_sleep'])
    mood_df['date_sleep'] = pd.to_datetime(mood_df['date_sleep'])
    merged = pd.merge(hrv_data, mood_df, how='inner', on='date_sleep')

    mens_cycles, error = create_every_cycles(menstrual_dates)
    hrv_tag = cal_hrv_tag(hrv_data, mens_cycles[0])
    hrv_diff_value = hrv_tag['hrv_pattern']
    hrv_trend = "符合" if hrv_diff_value >0 else "不符"
    hrv_diff = "高于" if hrv_diff_value >0 else "低于"

    pos_hrv_mood = merged[(merged['mood'] == 1) & (merged['mood'] == 2)]['hrv_avg']
    neg_hrv_mood = merged[(merged['mood'] == 3) & (merged['mood'] == 4 )& (merged['mood'] == 5 )]['hrv_avg']
    pos_mean = pos_hrv_mood.mean()
    neg_mean = neg_hrv_mood.mean()
    diff_ratio = abs(pos_mean - neg_mean) / max(pos_mean, neg_mean)

    if diff_ratio < 0.05:
       pos_desc = None
       neg_desc = None
    else:
        pos_desc = "高" if pos_mean > neg_mean else "低"
        neg_desc = "高" if neg_mean > pos_mean else "低"

    return { 'hrv_diff' : hrv_diff,
             'hrv_trend':hrv_trend,
             'pos_des': pos_desc,
             'neg_des':neg_desc

    }


def cal_symptoms_params(user_data) :

    pain_values = user_data.get('user_pain_records', [])
    pain_total_cycles = user_data.get('user_pain_total_cycles', [])
    symptom_records = user_data.get('user_symptom_records', {})


    n = len(pain_values)
    if n == 0:
        return "无痛经数据可分析。"

    # 1. 峰值痛经在哪段（前中后）
    max_val = max(pain_values)
    peak_days = [i for i, val in enumerate(pain_values) if val == max_val]
    peak_day = max(peak_days)  # 取最晚的那天

    ratio = peak_day / n
    if ratio <= 1 / 3:
        pain_phase = "前半段"
    elif ratio <= 2 / 3:
        pain_phase = "中段"
    elif ratio <= 1:
        pain_phase = "整段"
    else:
        pain_phase = "后半段"

    # 2. 持续几天得到缓解（从峰值开始算，直到疼痛≤1）
    relieve_day = n  # 默认整个周期都没缓解
    for i in range(peak_day, n):
        if pain_values[i] <= 1.0:
            relieve_day = i - peak_day + 1  # 包含开始那天
            break

    # 3. 是否符合典型痛经模式
    # 典型模式是：第一天达到高峰，持续2~3天后缓解
    typical = False
    if n >= 4:
        peak_first_3 = max(pain_values[:3])
        if pain_values[0] == peak_first_3 and all(pain_values[i] <= 1.0 for i in range(3, n)):
            typical = True

    typical_text = "基本符合" if typical else "不符合"

    if all(pain_total_cycles[i] < pain_total_cycles[i + 1] for i in range(len(pain_total_cycles) - 1)):
        pain_type = "加重"
        pain_trend = "恶化"
    elif all(pain_total_cycles[i] > pain_total_cycles[i + 1] for i in range(len(pain_total_cycles) - 1)):
        pain_type = "减轻"
        pain_trend = "改善"
    else:
        pain_type = None
        pain_trend = None

    result = []
    for item in symptom_records:
        total = sum(item[key] for key in ['mens', 'folli', 'ovula', 'lm'] if item[key] > 0)
        result.append({'symptom': item['symptom'], 'sum': total})
    sorted_result = sorted(result, key=lambda x: x['sum'], reverse=True)
    max_symptom = sorted_result[0]['symptom']

    result = {k: sum(v for v in vals.values() if v > 0) for k, vals in symptom_records.items()}

    # 找出最大值对应的 key
    sorted_items = sorted(result.items(), key=lambda x: x[1], reverse=True)

    # 最大和第二大
    max_symptom_key, max_value = sorted_items[0]
    second_symptom_key, second_value = sorted_items[1] if len(sorted_items) > 1 else (None, None)

    max_symptom_data = symptom_records[max_symptom_key]
    max_symptom_values = sorted(max_symptom_data.items(), key=lambda x: x[1], reverse=True)
    max_period_key, max_value = max_symptom_values[0]
    second_period_key, second_value = max_symptom_values[1] if len(sorted_items) > 1 else (None, None)


    symptom_value = {
            'pain_phase': pain_phase,
            'relieve_day': relieve_day,
            'typical_text':typical_text,
            'pain_type':pain_type,
            'pain_trend': pain_trend,
            'max_symptom': max_symptom_key,
            'other_symptom': second_symptom_key,
            'max_symptom_period': max_period_key,
            'second_symptom_period': second_period_key

            }

    return symptom_value

"""

